#include "wl_timer.h"

#include "wl_list.h"
#include "wl_malloc.h"
#include "wl_mutex.h"
#include "wl_timer_inner.h"
#include "wl_trace.h"
#include "wl_utils.h"

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#    include <os_api.h>
#    include <sci_types.h>
#elif defined(WL_OS_QUECTEL)
#    include "ql_rtos.h"
#else  // default linux
#    include <pthread.h>
#    include <sys/prctl.h>
#    include <time.h>
#    include <unistd.h>
#endif

#define WL_TIMER_NAME_LEN 40

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

typedef struct __wl_timer_message {
    WL_TIMER timer;
} wl_timer_message;

typedef struct {
    wl_uint32     timerid;
    wl_int32      taskid;
    WL_TIMER_TYPE type;
    wl_uint32     interval;
    wl_int32      lifecycle;  //生命期 小于等于0响应
    wl_char       name[WL_TIMER_NAME_LEN];
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    SCI_TIMER_PTR timer;
#elif defined(WL_OS_QUECTEL)
    ql_timer_t timer;
#endif
    wl_timer_callback callback;
    void*             context;
} wl_timer_info;

typedef struct {
    wl_bool   initialized;
    wl_list   timer_list;
    WL_MUTEX  mutex;
    wl_bool   exit;
    wl_uint32 current_timerid;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    BLOCK_ID      thread;
#elif defined(WL_OS_QUECTEL)
    ql_task_t  thread;
    ql_queue_t msg_queue;
#else
    pthread_t thread;
#endif
} wl_timer_global;

/*-------------------------------------------------------------------------
模块内部全局变量
-------------------------------------------------------------------------*/
static wl_timer_global s_timer_global;

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
void timer_callback(uint32 timerid) {
    SCI_SendSignal((void*)(timerid), s_timer_global.thread);
    WLLOGT("timer %d callback", timerid);
}

void timer_main_thread(uint32 argc, void* param) {
    wl_int32 timerid = 0;
    wl_list_iterator iterator;
    wl_timer_info* timer_info = wl_null;
    wl_task_message message;
    wl_timer_message timer_message;
    BLOCK_ID thread = SCI_IdentifyThread();

    WLLOGI("timer_main_thread start");
    while (!s_timer_global.exit) {
        timerid = (wl_int32)SCI_GetSignal(thread);
        WLLOGT("timer %d signal", timerid);

        wl_mutex_acquire(&s_timer_global.mutex);
        timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            if (timerid == timer_info->timerid) {
                if (REPETITIVE == timer_info->type) {
                    timer_info->lifecycle = timer_info->interval;
                } else {
                    timer_info->lifecycle = 0;
                }
                WLLOGT("timer %d name %s interval %d", timerid, timer_info->name, timer_info->interval);
                memset(&message, 0, sizeof(wl_task_message));
                timer_message.timer = timer_info->timerid;
                message.type = WL_TASK_MSG_TIMER;
                memcpy(&message.data, &timer_message, sizeof(wl_timer_message));
                message.datalen = sizeof(wl_timer_message);
                wl_send_task_message(timer_info->taskid, &message);
                break;
            }
            timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
        }
        wl_mutex_release(&s_timer_global.mutex);
        //定时器不存在
        if (wl_null == timer_info) {
            WLLOGW("timer %d not exist", timerid);
        }
    }
    SCI_ThreadExit();
}
#elif defined(WL_OS_QUECTEL)
void timer_callback(wl_uint32 timerid) {
    if (s_timer_global.initialized && !s_timer_global.exit) {
        ql_rtos_queue_release(s_timer_global.msg_queue, sizeof(wl_uint32), (wl_uint8*)&timerid, QL_NO_WAIT);
    }
}

void timer_main_thread(void* param) {
    wl_int32 timerid = 0;
    wl_list_iterator iterator;
    wl_timer_info* timer_info = wl_null;
    wl_task_message message;
    wl_timer_message timer_message;

    while (!s_timer_global.exit) {
        if (0
            == ql_rtos_queue_wait(s_timer_global.msg_queue, (wl_uint8*)&timerid, sizeof(wl_uint32), QL_WAIT_FOREVER)) {
            wl_mutex_acquire(&s_timer_global.mutex);
            timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
            while (!wl_list_iterator_is_end(&iterator)) {
                if (timerid == timer_info->timerid) {
                    if (REPETITIVE == timer_info->type) {
                        timer_info->lifecycle = timer_info->interval;
                    } else {
                        timer_info->lifecycle = 0;
                    }
                    WLLOGT("timer %d name %s interval %d", timerid, timer_info->name, timer_info->interval);
                    memset(&message, 0, sizeof(wl_task_message));
                    timer_message.timer = timer_info->timerid;
                    message.type = WL_TASK_MSG_TIMER;
                    memcpy(&message.data, &timer_message, sizeof(wl_timer_message));
                    message.datalen = sizeof(wl_timer_message);
                    wl_send_task_message(timer_info->taskid, &message);
                    break;
                }
                timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
            }
            wl_mutex_release(&s_timer_global.mutex);
        }
        WLLOGT("timer %d signal", timerid);

        //定时器不存在
        if (wl_null == timer_info) {
            WLLOGW("timer %d not exist", timerid);
        }
    }

    ql_rtos_task_delete(s_timer_global.thread);
    ql_rtos_queue_delete(s_timer_global.msg_queue);
}
#else
void timer_clock(wl_int32 clock) {
    wl_list_iterator iterator;
    wl_timer_info* timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer_info->lifecycle > 0) {
            timer_info->lifecycle -= clock;
            if (0 >= timer_info->lifecycle) {
                wl_task_message message;
                wl_timer_message timer_message;
                memset(&message, 0, sizeof(wl_task_message));
                timer_message.timer = timer_info->timerid;
                message.type = WL_TASK_MSG_TIMER;
                memcpy(&message.data, &timer_message, sizeof(wl_timer_message));
                message.datalen = sizeof(wl_timer_message);
                wl_send_task_message(timer_info->taskid, &message);
                if (REPETITIVE == timer_info->type) {
                    timer_info->lifecycle = timer_info->interval;
                }
            }
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
}

void* timer_main_thread(void* param) {
    UNUSED(param);
    WLLOGT("timer start");

    prctl(PR_SET_NAME, "timer_main_thread");

    while (!s_timer_global.exit) {
        wl_mutex_acquire(&s_timer_global.mutex);
        timer_clock(100);
        wl_mutex_release(&s_timer_global.mutex);
        wl_msleep(100);
    }

    pthread_exit(wl_null);
    WLLOGT("timer exit");
    return wl_null;
}
#endif

wl_int32 wl_timer_init(void) {
    WLLOGI("wl_timer_init");
    if (!s_timer_global.initialized) {
        if (0 != wl_mutex_create("wlsdk_timer", &s_timer_global.mutex)) {
            WLLOGW("wl_mutex_create fail");
            return -1;
        }
        wl_list_init(&s_timer_global.timer_list);
        s_timer_global.exit = wl_false;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        s_timer_global.thread = SCI_CreateThread(
            "wl_timer", "wl_timer_queue", timer_main_thread, 0, wl_null, 4 * 1024, 30, 20, SCI_PREEMPT, SCI_AUTO_START);
        if (SCI_INVALID_BLOCK_ID == s_timer_global.thread) {
            wl_mutex_delete(s_timer_global.mutex);
            WLLOGW("SCI_CreateThread fail");
            return -1;
        }
#elif defined(WL_OS_QUECTEL)
        ql_rtos_queue_create(&s_timer_global.msg_queue, sizeof(wl_uint32), 50);

        if (0 != ql_rtos_task_create(&s_timer_global.thread, 4 * 1024, 99, "wl_timer", timer_main_thread, wl_null)) {
            ql_rtos_queue_delete(s_timer_global.msg_queue);
            wl_mutex_delete(s_timer_global.mutex);
            WLLOGW("ql_rtos_task_create fail");
            return -1;
        }
#else
        pthread_attr_t attr;
        if (0 != pthread_attr_init(&attr)) {
            WLLOGW("pthread_attr_init fail");
            wl_mutex_delete(&s_timer_global.mutex);
            return -1;
        }
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        if (0 != pthread_create(&s_timer_global.thread, &attr, timer_main_thread, wl_null)) {
            pthread_attr_destroy(&attr);
            wl_mutex_delete(&s_timer_global.mutex);
            WLLOGW("pthread_create fail");
            return -1;
        }
        pthread_attr_destroy(&attr);
        WLLOGW("wl_timer_init success");
#endif
        s_timer_global.current_timerid = WL_INVALID_TIMER;
        s_timer_global.initialized     = wl_true;
    }
    return 0;
}

void wl_timer_end(void) {
    if (s_timer_global.initialized) {
        s_timer_global.exit = wl_true;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        //释放定时器
        {
            wl_list_iterator iterator;
            wl_timer_info* timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
            while (!wl_list_iterator_is_end(&iterator)) {
                SCI_DeleteTimer(timer_info->timer);
                timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
            }
        }
#elif defined(WL_OS_QUECTEL)
        {
            wl_list_iterator iterator;
            wl_timer_info* timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
            while (!wl_list_iterator_is_end(&iterator)) {
                ql_rtos_timer_delete(timer_info->timer);
                timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
            }
        }
#else
        pthread_join(s_timer_global.thread, wl_null);
#endif
        wl_mutex_acquire(&s_timer_global.mutex);
        wl_list_free(&s_timer_global.timer_list);
        wl_mutex_release(&s_timer_global.mutex);
        wl_mutex_delete(&s_timer_global.mutex);
        s_timer_global.initialized = wl_false;
    }
}

wl_int32 wl_timer_process_message(wl_task_message* message) {
    wl_timer_message  timer_message;
    wl_list_iterator  iterator;
    wl_timer_info*    timer_info = wl_null;
    wl_timer_callback callback   = wl_null;
    void*             context    = wl_null;
    if (wl_null == message || sizeof(wl_timer_message) != message->datalen) {
        WLLOGW("message is null");
        return -1;
    }
    memcpy(&timer_message, &(message->data), message->datalen);
    wl_mutex_acquire(&s_timer_global.mutex);
    timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer_message.timer == timer_info->timerid) {
            callback = timer_info->callback;
            context  = timer_info->context;
            //定时器释放
            if (0 >= timer_info->lifecycle) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
                SCI_DeleteTimer(timer_info->timer);
#elif defined(WL_OS_QUECTEL)
                ql_rtos_timer_delete(timer_info->timer);
#endif
                wl_free(timer_info);
                wl_list_iterator_remove(&iterator);
            }
            break;
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
    wl_mutex_release(&s_timer_global.mutex);

    if (callback) {
        callback(timer_message.timer, context);
    }
    return 0;
}

wl_uint32 wl_timer_create_timerid(void) {
    wl_uint32 timerid = WL_INVALID_TIMER;  // 0表示无效
    while (wl_true) {
        wl_list_iterator iterator;
        wl_timer_info*   timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
        timerid                     = ++s_timer_global.current_timerid;
        while (!wl_list_iterator_is_end(&iterator)) {
            // timerid已使用或为0
            if (WL_INVALID_TIMER == s_timer_global.current_timerid
                || s_timer_global.current_timerid == timer_info->timerid) {
                timerid = WL_INVALID_TIMER;
                break;
            }
            timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
        }
        if (WL_INVALID_TIMER != timerid) {
            break;
        }
    }
    return timerid;
}

WL_TIMER
wl_timer_start(wl_uint32         interval,
               WL_TIMER_TYPE     type,
               wl_timer_callback callback,
               void*             context,
               wl_int32          taskid,
               wl_char*          timer_name) {
    wl_timer_info* timer_info;

    wl_mutex_acquire(&s_timer_global.mutex);
    timer_info = (wl_timer_info*)wl_malloc(sizeof(wl_timer_info));
    if (wl_null != timer_info) {
        memset(timer_info, 0, sizeof(wl_timer_info));
        timer_info->timerid   = wl_timer_create_timerid();
        timer_info->taskid    = taskid;
        timer_info->type      = type;
        timer_info->interval  = interval;
        timer_info->lifecycle = interval;
        timer_info->callback  = callback;
        timer_info->context   = context;
        if (timer_name) {
            strncpy(timer_info->name, timer_name, sizeof(timer_info->name) - 1);
            timer_info->name[sizeof(timer_info->name) - 1] = '\0';
        }
        WLLOGT("wl_timer_start timer %s", timer_info->name);
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        timer_info->timer =
            SCI_CreatePeriodTimer("wlsdk_timer", timer_callback, timer_info->timerid, interval, SCI_NO_ACTIVATE);
        if (SCI_NULL == timer_info->timer) {
            WLLOGW("SCI_CreatePeriodTimer fail");
            wl_free(timer_info);
            wl_mutex_release(&s_timer_global.mutex);
            return WL_INVALID_TIMER;
        }
        if (SCI_SUCCESS != SCI_ActiveTimer(timer_info->timer)) {
            WLLOGW("SCI_ActiveTimer fail");
            SCI_DeleteTimer(timer_info->timer);
            wl_free(timer_info);
            wl_mutex_release(&s_timer_global.mutex);
            return WL_INVALID_TIMER;
        }
#elif defined(WL_OS_QUECTEL)
        if (0 != ql_rtos_timer_create(&timer_info->timer)) {
            WLLOGW("ql_rtos_timer_create fail");
            wl_free(timer_info);
            wl_mutex_release(&s_timer_global.mutex);
            return WL_INVALID_TIMER;
        }

        if (0
            != ql_rtos_timer_start(
                timer_info->timer, interval, NON_REPETITIVE == type ? 0 : 1, timer_callback, timer_info->timerid)) {
            ql_rtos_timer_delete(timer_info->timer);
            WLLOGW("ql_rtos_timer_start fail");
            wl_free(timer_info);
            wl_mutex_release(&s_timer_global.mutex);
            return WL_INVALID_TIMER;
        }
#endif

        if (-1 == wl_list_add(&s_timer_global.timer_list, timer_info, -1)) {
            WLLOGW("wl_list_add fail");
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
            SCI_DeleteTimer(timer_info->timer);
#elif defined(WL_OS_QUECTEL)
            ql_rtos_timer_delete(timer_info->timer);
#endif
            wl_free(timer_info);
            wl_mutex_release(&s_timer_global.mutex);
            return wl_null;
        }
        WLLOGI("wl_timer_start timer count %d", wl_list_size(&s_timer_global.timer_list));
    }
    wl_mutex_release(&s_timer_global.mutex);
    WLLOGT("wl_timer_start success");
    return timer_info->timerid;
}

wl_int32 wl_timer_stop(WL_TIMER timer) {
    wl_timer_info*   timer_info;
    wl_list_iterator iterator;

    wl_mutex_acquire(&s_timer_global.mutex);
    timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer == timer_info->timerid) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
            SCI_DeleteTimer(timer_info->timer);
            WLLOGT("wl_timer_stop timer %s", timer_info->name);
#elif defined(WL_OS_QUECTEL)
            ql_rtos_timer_delete(timer_info->timer);
#endif
            wl_free(timer_info);
            wl_list_iterator_remove(&iterator);
            break;
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
    WLLOGI("wl_timer_stop timer count %d", wl_list_size(&s_timer_global.timer_list));
    wl_mutex_release(&s_timer_global.mutex);
    WLLOGT("wl_timer_stop end");
    return 0;
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
